;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;mini-standard-input.lisp
;;
;;  copyright © 1987-89 Apple Computer
;;
;;  This file defines a stream that can be used for *standard-input*
;;  in stand-alone applications.  This is not a stand-alone application.
;;  Rather, it is a tool useful in stand-alone applications which use the
;;  the functions read, read-line, and read-char.
;;
;;  *standard-input* normally reads from *terminal-io*.  However, this
;;  won't work in stand-alone applications, because *terminal-io* uses
;;  Fred the editor, and most of Fred is removed from stand-alone applications.
;;
;;  By default, the functions read, read-line, and read-char read from
;;  *standard-input*.  This won't work in stand-alone applications unless
;;  you set *standard-input* to something besides *terminal-io*, something
;;  which doesn't use Fred.  Such a stream is defined in this file.
;;
;;  The stream in this file is modal, so it doesn't work very well for Lisp
;;  programming, where read is being called all the time.  However, in a
;;  stand-alone application (which probably doesn't constantly call read),
;;  this should work fine.
;;
;;  The stream maintains a buffer, from which it reads.  Whenever the buffer
;;  gets empty, the stream puts up a dialog box to get more text for the buffer.
;;



;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; define the class, *mini-input*
;;

(defobject *mini-input* *stream*)
;;;;;;;;;;;;;;;;;;;;;;;
;;
;; define exist for the class
;;

(defobfun (exist *mini-input*) (init-list)
  (have 'input-buffer "")
  (have 'input-buffer-pointer 0)
  (have 'untied-p nil))

;;;;;;;;;;;;;;
;;
;;  this string is used by a prompt whenever the stream needs more text
;;

(defvar *standard-input-message* "Please enter some input.")

;;;;;;;;;;;;;
;;
;;  this is the function which gets more text whenever the buffer is empty
;;

(defobfun (get-some-input *mini-input*) ()
  (setq input-buffer
        (concatenate 'string
                     (get-input-dialog 
                      *standard-input-message*)
                     (string #\space))
        input-buffer-pointer 0))

;;;;;;;;;;;;;;
;;
;;  this dialog is used by the preceding function
;;

(defun get-input-dialog (message)
  (let ((dial
         (oneof *dialog*
                :window-show nil
                :default-button nil
                :window-type :double-edge-box
                :window-size #@(300 200)
                :window-position (make-point
                                  (ash (- *screen-width*
                                          300)
                                       -1)
                                  (+ *menubar-bottom* 20)))))
    (ask dial
      (add-dialog-items
       (oneof *static-text-dialog-item*
              :dialog-item-text message
              :dialog-item-position #@(4 176))
       (have 'text-item
             (oneof *editable-text-dialog-item*
                    :dialog-item-position #@(4 4)
                    :dialog-item-size #@(290 162)
                    :allow-returns t))
       (oneof *button-dialog-item*
              :dialog-item-position #@(236 176)
              :dialog-item-size #@(60 18)
              :dialog-item-text "Enter"
              :dialog-item-action
              #'(lambda ()
                  (ask my-dialog
                    (return-from-modal-dialog
                     (ask text-item (dialog-item-text))))))))
    (modal-dialog dial)))

;;;;;;;;;;;;;;;;
;;
;;  stream-tyi returns the first character in the buffer.
;;  if the buffer is empty, it calls get-some-input to get some more
;;  text for the buffer

(defobfun (stream-tyi *mini-input*) ()
  (when (>= input-buffer-pointer (length input-buffer))
    (get-some-input))
  (prog1
    (elt input-buffer input-buffer-pointer)
    (setq input-buffer-pointer (+ 1 input-buffer-pointer))
    (setq untied-p nil)))

;;;;;;;;;;;;;
;;
;;  because of the way the stream works, it will never be at end of file
;;

(defobfun (stream-eofp *mini-input*) ()
  nil)

;;;;;;;;;;;;;
;;
;;  stream-untyi decrements the pointer into the buffer
;;

(defobfun (stream-untyi *mini-input*) (char)
  (when untied-p (error "Can't untyi twice in a row."))
  (if (= input-buffer-pointer 0)
      (setq input-buffer (concatenate 'string
                                      (string char)
                                      input-buffer))
      (setq input-buffer-pointer (- input-buffer-pointer 1)))
  (setq untied-p t))

;;;;;;;;;;;;;;;;;
;;
;;  stream-clear-input sets the buffer to be empty
;;

(defobfun (stream-clear-input *mini-input*) ()
  (setq input-buffer ""
        input-buffer-pointer 0))

#|
;;  Once the above forms have been defined, the first form below will set
;;  *standard-input* to an instance of the *mini-input* class.
;;
;;  You probably only want to do this inside an application.  In the normal
;;  Pearl environment, *mini-input* is very modal.  It just puts up a modal
;;  dialog, into which you type forms (just like the Listener, but much less
;;  flexible).  The results are still printed in the Listener.
;;
;;  To get out of this situation, type the second form below into the
;;  mini-input window.


;this sets up mini-input
(setq *standard-input* (oneof *mini-input*))

;this will get you out of mini-input
(setq *standard-input* *terminal-io*)
|#